import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.List;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;

import javax.security.auth.Refreshable;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

public class LifeGameControler {

	private LifeGameModel _model;
	private Thread _thread;
	private int _timelaps;
	private boolean _paused;
	private JButton btnPause;
	private JButton btnGenesis;
	private JButton btnReset;
	private JButton btnSave;
	private JButton btnLoad;
	private JSlider sldWidht;
	private JSlider sldHeigth;
	private JSlider sldTimelaps;
	private JSlider sldLife;
	private JPanel pnlGame;
	private JCheckBox chkLifeRule;
	private LinkedList<JPanel> gameCells;
	private boolean _mousePressed;

	public boolean is_paused() {
		return _paused;
	}

	public LifeGameControler(LifeGameModel model)
	{
		_timelaps = 100;
		_model = model;
		_paused = false;
		gameCells = new LinkedList<JPanel>();
		_thread = new Thread(new Runnable() {
			@Override
			public void run() {
				while(true)
				{
					if(_paused == false)
						synchronized (_model) {
							_model.Iterate();
						}
					try {
						Thread.sleep(_timelaps);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		_thread.start();
	}

	public void setSliders(JSlider widht, JSlider heigth)
	{
		sldHeigth = heigth;
		sldWidht = widht;
		sldHeigth.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent arg0) {
				synchronized(_model)
				{
					ArrayList<Cell> cells = (ArrayList<Cell>) _model.get_cells().clone();
					int _widht = _model.get_widht();
					int _heigth = _model.get_heigth();
					_model.set_heigth(sldHeigth.getValue());
					_model.Regenerate();
					for(int y = 0; (y < _heigth) && (y < _model.get_heigth()); y ++)
					{
						for(int x = 0; (x < _widht) && (x < _model.get_widht()); x ++)
						{
							_model.set_cell(cells.get(y * _widht + x), x, y);
						}
					}
					pnlGame.revalidate();
					pnlGame.repaint();
				}
			}
		});
		sldWidht.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				synchronized(_model)
				{
					ArrayList<Cell> cells = (ArrayList<Cell>) _model.get_cells().clone();
					int _widht = _model.get_widht();
					int _heigth = _model.get_heigth();
					_model.set_widht(sldWidht.getValue());
					_model.Regenerate();
					for(int y = 0; (y < _heigth) && (y < _model.get_heigth()); y ++)
					{
						for(int x = 0; (x < _widht) && (x < _model.get_widht()); x ++)
						{
							_model.set_cell(cells.get(y * _widht + x), x, y);
						}
					}
					pnlGame.revalidate();
					pnlGame.repaint();
				}
			}
		});
	}

	public void setSaveButton(JButton button)
	{
		btnSave = button;
		btnSave.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				// ouverture de l'encodeur vers le fichier
				XMLEncoder encoder;
				try {
					JFileChooser chooser = new JFileChooser(); 
					chooser.setDialogTitle("Enregistrer");
					chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 
					FileNameExtensionFilter xmlfilter = new FileNameExtensionFilter(
							"xml files (*.xml)", "xml");
					chooser.setFileFilter(xmlfilter);
					if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) 
					{ 
						String filename = chooser.getSelectedFile().getPath().concat(".xml");
						encoder = new XMLEncoder(new FileOutputStream(filename));
						try {
							synchronized (_model) {
								encoder.writeObject(_model);
								encoder.flush();
							}
						} finally {
							encoder.close();
						}
					}
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}
		});
	}

	public void setLoadButton(JButton button)
	{
		btnLoad = button;
		btnLoad.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// ouverture de decodeur
				XMLDecoder decoder;
				try {
					JFileChooser chooser = new JFileChooser(); 
					chooser.setDialogTitle("Charger");
					chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 
					FileNameExtensionFilter xmlfilter = new FileNameExtensionFilter(
							"xml files (*.xml)", "xml");
					chooser.setFileFilter(xmlfilter);
					if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) 
					{
						String filename = chooser.getSelectedFile().getPath();
						decoder = new XMLDecoder(new FileInputStream(filename));
						try {
							synchronized (_model) {
								LifeGameModel model = (LifeGameModel) decoder.readObject();
								_model.set_widht(model.get_widht());
								_model.set_heigth(model.get_heigth());
								_model.set_cells(model.get_cells());
								sldHeigth.setValue(_model.get_heigth());
								sldWidht.setValue(_model.get_widht());
							}
							_model.Refresh();
							pnlGame.revalidate();
							pnlGame.repaint();
						} finally {
							decoder.close();
						}
					}
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				}
			}
		});
	}

	public void setLifeSlider(JSlider slider)
	{
		sldLife = slider;
		sldLife.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				synchronized(_model)
				{
					LifeGameModel.LIFE = sldLife.getValue();
					for (Cell cell : _model.get_cells()) {
						if(cell.get_life() > LifeGameModel.LIFE)
							cell.set_life(LifeGameModel.LIFE);
					}
				}
			}
		});
	}

	public void setLifeRuleCheckBox(JCheckBox checkbox)
	{
		chkLifeRule = checkbox;
		checkbox.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				synchronized(_model)
				{
					_model.ToogleLifeRule();
				}
			}
		});
	}

	public void setGamePanel(JPanel panel)
	{
		pnlGame = panel;
	}

	public void setGenesisButton(JButton button)
	{
		btnGenesis = button;
		button.addActionListener( new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				synchronized (_model) {
					Genesis();
				}
			}
		});
	}

	public void setResetButton(JButton button)
	{
		btnReset = button;
		button.addActionListener( new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				synchronized (_model) {
					clearCells();
				}
			}
		});
	}

	public void setPauseButton(JButton button)
	{
		btnPause = button;
		button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				synchronized (_model) {
					tooglePause();
					if(is_paused())
						btnPause.setText("Démarer");
					else
						btnPause.setText("Mettre en pause");
				}
			}
		});
	}

	public void setTimelapsSlider(JSlider timelaps)
	{
		sldTimelaps = timelaps;
		sldTimelaps.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent arg0) {
				synchronized(_model)
				{
					_timelaps = sldTimelaps.getValue();
				}
			}
		});
	}

	public void createCells()
	{
		gameCells.clear();
		pnlGame.removeAll();
		pnlGame.setLayout(new GridLayout(_model.get_heigth(),_model.get_widht(),1,1));
		for(int y = 0; y < _model.get_heigth(); y++)
		{
			for(int x = 0; x < _model.get_widht(); x++)
			{
				addCell(new JPanel(false));
			}
		}
	}

	public void addCell(JPanel cell)
	{
		gameCells.add(new JPanel(new BorderLayout(1,1),true));
		JPanel c = gameCells.getLast();
		c.setBackground(new Color(128,128,128));
		c.setPreferredSize(new Dimension(5, 5));
		c.addMouseListener(new MouseListener() {

			@Override
			public void mouseReleased(MouseEvent e) {
				_mousePressed = false;
			}

			@Override
			public void mousePressed(MouseEvent e) {
				_mousePressed = true;
				JPanel panel = (JPanel) e.getSource();
				int x, y;
				x = gameCells.indexOf(panel) % _model.get_widht();
				y = (gameCells.indexOf(panel) - x) / _model.get_widht();
				toogleCell(x, y);
				Cell cell = _model.get_cell(x, y);
				if(cell.is_alive() && (panel.getBackground() != Color.RED))
					panel.setBackground(Color.RED);
				else if((!cell.is_alive()) && (panel.getBackground() != Color.WHITE))
					panel.setBackground(Color.WHITE);
			}

			@Override
			public void mouseExited(MouseEvent e) {
			}

			@Override
			public void mouseEntered(MouseEvent e) {
				if(_mousePressed)
				{
					JPanel panel = (JPanel) e.getSource();
					int x, y;
					x = gameCells.indexOf(panel) % _model.get_widht();
					y = (gameCells.indexOf(panel) - x) / _model.get_widht();
					toogleCell(x, y);
					Cell cell = _model.get_cell(x, y);
					if(cell.is_alive() && (panel.getBackground() != Color.RED))
						panel.setBackground(Color.RED);
					else if((!cell.is_alive()) && (panel.getBackground() != Color.WHITE))
						panel.setBackground(Color.WHITE);
				}
			}

			@Override
			public void mouseClicked(MouseEvent e) {
			}
		});
		pnlGame.add(c);
	}

	public void tooglePause()
	{
		_paused = !_paused;
	}

	public void updateCells()
	{
		if(gameCells.size() != _model.get_cells().size())
			createCells();
		pnlGame.setIgnoreRepaint(true);
		ArrayList<Cell> cells = _model.get_cells();
		int i = 0;
		for (Cell cell : cells) {
			if(cell.is_alive())
			{
				int g = (int) (510 * ((float)cell.get_life()/LifeGameModel.LIFE));
				Color c;
				if(g < 256)
					c = new Color(255, g, 0);
				else
					c = new Color(255 - (g - 256), 255, 0);
				gameCells.get(i).setBackground(c);
			}
			else if((!cell.is_alive()) && (gameCells.get(i).getBackground() != Color.WHITE))
				gameCells.get(i).setBackground(Color.WHITE);
			i++;
		}
		pnlGame.setIgnoreRepaint(false);
	}

	public void toogleCell(int x, int y)
	{
		synchronized(_model)
		{
			_model.toogleCell(x,y);
		}
	}

	public void clearCells()
	{
		_model.ClearCells();
		_model.Refresh();
		pnlGame.revalidate();
		pnlGame.repaint();
	}

	public void Genesis()
	{
		_model.Genesis();
		_model.Refresh();
		pnlGame.revalidate();
		pnlGame.repaint();
	}

}
